﻿/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 * ***************************************************************************/



#pragma once
#include <math.h>

/*! \brief A GDI+ Image Renderer for MFC ISAPI Extensions

\par Description

<p>This class enables you to render GDI+ image on the server and send them directly to the client browser
without passing by temporarly files.</p>

\par Basic use
<p>The use of the class is straight forward: build a CGDIpISAPI object with all the pointers and call WriteImage:</p>
\code
void CHttpServerDerviedClass::Default( CHttpServerContext* pCtxt)
{
	using namespace Gdiplus;

	// creating bitmap
	Bitmap bitmap(320,200);
	// drawing on this bitmap
	...
	
	// sending bitmap to the browser
	CGDIpISAPI renderer( this, pCtxt, &bitmap );
		// sending image
	renderer.WriteImage();
}
\endcode

\par Output options
<p>You can choose the image code (PNG, JPEG, BMP or TIFF) and change the quality ratio for the PNG and JPEG files.
The quality must be between 0 and 100.</p>
<p>All these options can be set at the construction or by setters.</p>
\code 
// Setting bmp type
renderer.SetImageType( CGDIpISAPI::ImageBMP);
\endcode
\code 
// Changing quality
renderer.SetQuality( 98 );
\endcode

\par Initializing GDI+
<p>Do not forget to initialize and deinitialize GDI+. I'm using a helper class for that: CGDIpInitializer.</p>
\code 
class CMyExtension
{
...
	CGDIpInitializer m_GDIpInitializer;
}
\endcode
\code
BOOL CMyExtension::GetExtensionVersion(HSE_VERSION_INFO* pVer)
{
	// Appelez l'impl閙entation par d閒aut pour l'initialisation
	CHttpServer::GetExtensionVersion(pVer);

	// Initialize GDI+
	m_GDIpInitializer.Initialize();
	...
\endcode
\code
BOOL CMyExtension::TerminateExtension(DWORD dwFlags)
{
	// l'extension est arr阾閑
	//TODO? nettoyez les ressources par instance
	m_GDIpInitializer.Deinitialize();
\endcode
\par What not to do...
<ul>
<li>When writing an image, do not write anything else!</li>
</ul>

\author Jonathan de Halleux, dehalleux@auto.ucl.ac.be
\ingroup PGLISAPIGroup
*/
class CGDIpISAPI
{
protected:
	//! \name Typdefs
	//@{
	//! Different supported image codecs
	static enum EImageType
	{
		//! Portable Network Graphics
		ImagePNG = 0,
		//! Famous JPEG
		ImageJPEG = 1,
		//! Bitmap
		ImageBMP=2,
	};
	//@}

	//! \name Setters
	//@{
	//! Sets the image type
	void SetImageType(EImageType eImageType)	{	m_eImageType = eImageType;};
	//! return the image type
	EImageType GetImageType() const				{	return m_eImageType;};
	//! returns CGI string describing the current image type
	LPCTSTR CGDIpISAPI::GetImageTypeString() const;
	//@}

public:
	//! \name Constructors
	//@{
	/*! \brief Default constructor

	  \param pServer pointer to the CHttpServer class
	  \param pCtxt pointer to the CHttpServerContext object
	  \param pImage pointer to a GDI+ image object
	  \param eImageType image type (default is PNG)
	  \param ulQuality quality. Applies to JPEG (default is 80)

	  The quality must be an integer between 0 (ugly, good compression) and 100 (nice, poor compression).
	 */
	CGDIpISAPI(CHttpServer* pServer=NULL, 
		CHttpServerContext* pCtxt=NULL,
		Gdiplus::Image* pImage=NULL, EImageType eImageType = ImagePNG, long lJPEGQuality = 80)
	: m_pServer(pServer),m_pCtxt(pCtxt), m_pImage(pImage), m_eImageType(ImagePNG)
	{	SetJPEGQuality(lJPEGQuality);};
	virtual ~CGDIpISAPI(){};
	//@}


	//! \name JPEG Options
	//@{
	/*! JPEG flags

	Note  If the width and height of a JPEG image are multiples of 16, 
	you can apply any of the transformations 
	(for example, 90-degree rotation) without loss of information.
	*/
	static enum EJPEGTransform
	{
		//! specifies no transformation
		JPEGTransformNone=0,
		//! specifies lossless 90-degree clockwise rotation
		JPEGTransformRotate90=Gdiplus::EncoderValueTransformRotate90,
		//!  specifies lossless 180-degree rotation
		JPEGTransformRotate180=Gdiplus::EncoderValueTransformRotate180,
		//! specifies lossless 270-degree clockwise rotation
		JPEGTransformRotate270=Gdiplus::EncoderValueTransformRotate270,
		//! specifies a lossless horizontal flip
		JPEGTransformFlipHorizontal=Gdiplus::EncoderValueTransformFlipHorizontal,
		//! specifies a lossless vertical flip. 
		JPEGTransformFlipVertical=Gdiplus::EncoderValueTransformFlipVertical
	};
	
	//! sets the jpeg transform, default is none
	void SetJPEGTransform( EJPEGTransform eTransform )	{	m_eJPEGTransform = eTransform;};
	//! return the current jpeg transform
	EJPEGTransform GetJPEGTransform() const				{	return m_eJPEGTransform; };

	/*! \brief Sets the quality

	Sets the quality of the JPEG from 0 (low quality, good compression) to 100 (high quality, low compression).
	*/
	void SetJPEGQuality( long lQuality )					{	m_lJPEGQuality = __min( 100, __max( 0, lQuality ));};
	//! return the jpeg quality
	long GetJPEGQuality() const								{	return m_lJPEGQuality;};
	//@}

	//! \name Output functions
	//@{
	//! Send the image to the brower
	void WriteImage();
	//! Writes info about the image
	void WriteInfo(void);
	//@}


protected:
	CHttpServer* m_pServer;
	CHttpServerContext* m_pCtxt;
	Gdiplus::Image* m_pImage;
	
private:
	//! \name Helpers
	//@{
	void DumpError(LPCTSTR pszFormat, ...);
	void WriteContext(LPCTSTR szFormat,...);
	int GetEncoderClsid(const WCHAR* format, CLSID* pClsid);
	void GetEncoderParameters(Gdiplus::EncoderParameters& encode);
	//@}


	EImageType m_eImageType;
	EJPEGTransform m_eJPEGTransform;
	long m_lJPEGQuality;
};
